######################################################################################
#
# This file was provided as part of the SharePoint Mystery Deployment package
# Created by Christopher Kimbell (http://sharepointmystery.com)
#
######################################################################################

# -----------------------------------------------------------------
# Creates an object used to manipulate a web application
# -----------------------------------------------------------------
function New-WebApplication()
{
    $obj=new-object PSObject -Property @{
     
      DB = New-Database;
      DefaultZone = New-Zone;
	  CustomZone = New-Zone;
	  IntranetZone = New-Zone;
	  InternetZone = New-Zone;
	  ExtranetZone = New-Zone;
      SC = New-SiteCollection;
      Pool = New-ApplicationPool;
      SearchBox = New-SearchBox;
      BlobCache = New-BlobCache;
	  SiteProvisiongSettings = New-SiteProvisioningSettings;
      Features = @();
      Solutions = @();
      ManagedPaths = @();
      SuperUserAccount = "";
      SuperReaderAccount = "";
	  ApplicationType = "";
	  EnableSSC = $false; # Self Site Create
    }

	$obj.DefaultZone.Type = "Default"
	$obj.CustomZone.Type = "Custom"
	$obj.IntranetZone.Type = "Intranet"
	$obj.InternetZone.Type = "Internet"
	$obj.ExtranetZone.Type = "Extranet"
    $obj.SC.WebApplication = $obj
    $obj.SearchBox.WebApplication = $obj
    $obj.BlobCache.WebApplication = $obj
	$obj.DefaultZone.WebApplication = $obj
	$obj.CustomZone.WebApplication = $obj
	$obj.IntranetZone.WebApplication = $obj
	$obj.InternetZone.WebApplication = $obj
	$obj.ExtranetZone.WebApplication = $obj
	$obj.SiteProvisiongSettings.WebApplication = $obj
	 
    # -----------------------------------------------------------------
    # Validates the information in the object
    # -----------------------------------------------------------------
    Add-Method $obj "Validate" `
    {
		$this.DefaultZone.Enabled = $true # default zone is always enabled
        Write-Operation "Validating Web Application"  
        $this.DB.Validate()
        $this.DefaultZone.Validate()
		
		if($this.InternetZone.Enabled -eq $true)
		{
			$this.InternetZone.Validate()
		}
		if($this.IntranetZone.Enabled -eq $true)
		{
			$this.IntranetZone.Validate()
		}
		if($this.ExtranetZone.Enabled -eq $true)
		{
			$this.ExtranetZone.Validate()
		}
		if($this.CustomZone.Enabled -eq $true)
		{
			$this.CustomZone.Validate()
		}	
        $this.SC.Validate()
        $this.Pool.Validate()
    }
	# -----------------------------------------------------------------
    # Prepares the server for deployment
    # -----------------------------------------------------------------
	Add-Method $obj "PrepareServers" `
	{
		# the following steps are done on each server. Other steps are done centrally, then SharePoint performs the operation on each server
		$this.BlobCache.CreateDirectory()
	
		New-SPMServerDirectory -Path $this.DefaultZone.AbsolutePath -Servers $farm.Servers
		Add-SPMHostsEntry -HostName $this.DefaultZone.HostHeader `
            -IPAddress "127.0.0.1" `
            -Comment "$($this.DefaultZone.ServerComment)" `
			-Servers $farm.Servers
        
		
		if($this.InternetZone.Enabled -eq $true)
		{
			New-SPMServerDirectory -Path $this.InternetZone.AbsolutePath -Servers $farm.Servers
			Add-SPMHostsEntry -HostName $this.InternetZone.HostHeader `
				-IPAddress "127.0.0.1" `
				-Comment "$($this.InternetZone.ServerComment)" `
				-Servers $farm.Servers
		}
		if($this.IntranetZone.Enabled -eq $true)
		{
			New-SPMServerDirectory -Path $this.IntranetZone.AbsolutePath -Servers $farm.Servers
			Add-SPMHostsEntry -HostName $this.IntranetZone.HostHeader `
				-IPAddress "127.0.0.1" `
				-Comment "$($this.IntranetZone.ServerComment)" `
				-Servers $farm.Servers
		}
		if($this.ExtranetZone.Enabled -eq $true)
		{
			New-SPMServerDirectory -Path $this.ExtranetZone.AbsolutePath -Servers $farm.Servers
			Add-SPMHostsEntry -HostName $this.ExtranetZone.HostHeader `
				-IPAddress "127.0.0.1" `
				-Comment "$($this.ExtranetZone.ServerComment)" `
				-Servers $farm.Servers
		}
		if($this.CustomZone.Enabled -eq $true)
		{
			New-SPMServerDirectory -Path $this.CustomZone.AbsolutePath -Servers $farm.Servers
			Add-SPMHostsEntry -HostName $this.CustomZone.HostHeader `
				-IPAddress "127.0.0.1" `
				-Comment "$($this.CustomZone.ServerComment)" `
				-Servers $farm.Servers
		}
	}
    # -----------------------------------------------------------------
    # Provisions the Web Application
    # -----------------------------------------------------------------
    Add-Method $obj "Provision" `
    {
        # the web application will automatically provision the application pool
        # do not provision explicitly
        #$this.Pool.Provision()
                 
        $webApp = Get-SPWebApplication | Where-Object {$_.DisplayName -eq $this.DefaultZone.ServerComment}
       
        if($webApp -ne $null)
        {
            Write-OperationDone "Web Application $($this.DefaultZone.AbsoluteUrl) already exists"
        }
        else
        {  
            $this.DB.Provision()
        
            # the arguments to New-SPWebApplication differ depending on if the pool exists or not. 
            $existingPool = Get-SPWebApplication | Where-Object {$_.ApplicationPool.Name -eq $this.Pool.Name}
            		
			Write-Operation "Provisioning Web Application $($this.DefaultZone.AbsoluteUrl)"
			
            if($existingPool -eq $null)
            {
                $webApp = New-SPWebApplication -Name $this.DefaultZone.ServerComment `
                        -Port $this.DefaultZone.Port `
                        -URL $this.DefaultZone.AbsoluteUrl `
                        -HostHeader $this.DefaultZone.HostHeader `
                        -ApplicationPool $this.Pool.Name `
                        -DatabaseName $this.DB.FullName `
                        -Path $this.DefaultZone.AbsolutePath  `
                        -AuthenticationProvider $this.DefaultZone.AuthenticationProvider `
                        -AuthenticationMethod $this.DefaultZone.AuthenticationMethod `
                        -ApplicationPoolAccount ($this.Pool.RegisterAccount())
            } 
            else
            {
                $webApp = New-SPWebApplication -Name $this.DefaultZone.ServerComment `
                        -Port $this.DefaultZone.Port `
                        -URL $this.DefaultZone.AbsoluteUrl `
                        -HostHeader $this.DefaultZone.HostHeader `
                        -ApplicationPool $this.Pool.Name `
                        -DatabaseName $this.DB.FullName `
                        -Path $this.DefaultZone.AbsolutePath `
                        -AuthenticationProvider $this.DefaultZone.AuthenticationProvider `
                        -AuthenticationMethod $this.DefaultZone.AuthenticationMethod
            }
        }
        
        if($farm.RegisterSPN -eq $true)
        {
            Add-SPMServicePrincipalName -Spn $this.DefaultZone.AbsoluteUrl -Account $this.Pool.Account
        }
		
		foreach ($path in $this.ManagedPaths) 
		{
			if ($path -ne "")
			{
				$mp = Get-SPManagedPath -WebApplication $this.DefaultZone.AbsoluteUrl | Where-Object {$_.Name -eq $path}
				if($mp -eq $null)
				{
					Write-Operation "Adding managed path $($path) to $($this.DefaultZone.AbsoluteUrl)"
					New-SPManagedPath -WebApplication $this.DefaultZone.AbsoluteUrl -RelativeURL $path
				}
				else
				{
					Write-OperationDone "Managed path $($path) already exists in $($this.DefaultZone.AbsoluteUrl)"
				}
			}
		}
        
        #set the super user accounts
        Set-SPMSuperUserAccounts -Identity $webApp `
            -SuperUserAccount $this.SuperUserAccount `
            -SuperReaderAccount $this.SuperReaderAccount
     
		$this.SiteProvisiongSettings.Configure()
       
		$webApp = $webApp.Clone() # prevents update conflict on the next line first time application is created
		
		if($webApp.SelfServiceSiteCreationEnabled -ne $this.EnableSSC)
		{
			$webApp.SelfServiceSiteCreationEnabled = $this.EnableSSC
			$webApp.Update()
		}
		
		$this.BlobCache.Configure()   
		
		#make sure that service applications have access to the content databases
		if($accessSvc -ne $null)
		{
			$webApp.GrantAccessToProcessIdentity($accessSvc.Pool.Account)
		}
		if($excelSvc -ne $null)
		{
			$webApp.GrantAccessToProcessIdentity($excelSvc.Pool.Account)
		}
		if($wordViewingSvc -ne $null)
		{
			$webApp.GrantAccessToProcessIdentity($wordViewingSvc.Pool.Account)
		}
		if($pptSvc -ne $null)
		{
			$webApp.GrantAccessToProcessIdentity($pptSvc.Pool.Account)
		}
    }
    # -----------------------------------------------------------------
    # Unprovisions the Web Application
    # -----------------------------------------------------------------
    Add-Method $obj "Unprovision" `
    {
        $webApp = Get-SPWebApplication  | Where-Object {$_.DisplayName -eq $this.DefaultZone.ServerComment}
        if($webApp -ne $null)
        {
            Write-Operation "Unprovisioning Web Application pool $($this.DefaultZone.AbsoluteUrl)"
            Remove-SPWebApplication -Identity $webApp -DeleteIISSite -RemoveContentDatabase -Confirm:$false
        }
        else
        {
            Write-OperationDone "Web Application pool $($this.DefaultZone.AbsoluteUrl) was already unprovisioned"
        }
    }
    # -----------------------------------------------------------------
    # Deploys the application scoped solutions
    # -----------------------------------------------------------------
    Add-Method $obj "DeploySolutions" `
    {        
        foreach ($name in $this.Solutions) 
        {
            $solution = Add-Solution $name
            if($solution.ContainsWebApplicationResource -eq $true)
            {
                $wa = Get-SPWebApplication -Identity $this.DefaultZone.AbsoluteUrl
                if($solution.DeployedWebApplications.Contains($wa) -eq $true)
                {
                    Write-OperationDone "Solution $($name) has already been deployed"
                }
                else
                {
                    Write-Operation "Deploying solution $(name)"
                    Install-SPSolution -Identity $name `
                        -WebApplication $this.DefaultZone.AbsoluteUrl `
                        -GACDeployment `
                        -CASPolicies
                    
                    Wait-Solution $name
                }
            }
            else
            {
                Write-Warning "Solution $($name) cannot be deployed to a web application since it's global"
            }
        }
    }
    # -----------------------------------------------------------------
    # Retracts the application scoped solutions
    # -----------------------------------------------------------------
    Add-Method $obj "RetractSolutions" `
    {       
        foreach ($name in $this.Solutions) 
        {
            $solution = Get-SPSolution | Where-Object {$_.Name -eq $name}
            if($solution -ne $null -and $solution.Deployed -eq $true)
            {
                if($solution.ContainsWebApplicationResource -eq $true)
                {
                    $wa = Get-SPWebApplication -Identity $this.DefaultZone.AbsoluteUrl
                    if($solution.DeployedWebApplications.Contains($wa) -eq $true)
                    {
                        Write-Operation "Retracting solution $($name)"
                        Uninstall-SPSolution -Identity $solution -WebApplication $wa -Confirm:$false
                        Wait-Solution $name
                    }
                }
                else
                {
                    Write-Warning "Solution $($name) cannot be retracted from a web application since it's global"
                }
                
                # if it's no longer deployed, we can remove it
                if($solution.Deployed -eq $false)
                {
                    Remove-SPSolution -Identity $solution -Confirm:$false
                }
            }
        }
    }
    # -----------------------------------------------------------------
    # Activates Web Application scoped features
    # -----------------------------------------------------------------
    Add-Method $obj "ActivateFeatures" `
    {    
        foreach ($name in $this.Features) 
        {
            $feature = Get-SPFeature -WebApplication $this.DefaultZone.AbsoluteUrl | Where-Object {$_.DisplayName -eq $name }
            if($feature -eq $null)
            {
                Write-Operation "Activating Feature $($name)"
                Enable-SPFeature -Identity $name -Url $this.DefaultZone.AbsoluteUrl
            }
            else
            {
                 Write-OperationDone "Feature $($name) was already activated at $($this.DefaultZone.AbsoluteUrl)"
            }  
        }
    }
    # -----------------------------------------------------------------
    # Perfoms upgrading of features
    # -----------------------------------------------------------------
    Add-Method $obj "UpgradeFeatures" `
    {
		Upgrade-SPMFeatures -WebApplication $this.DefaultZone.AbsoluteUrl
    }
    # -----------------------------------------------------------------
    # Starts a timer job
    # -----------------------------------------------------------------
    Add-Method $obj "StartTimer" `
    {
        params($name)
        
        $job = Get-SPTimerJob -WebApplication $this.DefaultZone.AbsoluteUrl -Identity $name
        if($job -ne $null)
        {
            Start-SPTimerJob -Identity $job -Confirm:$false
        }
        else
        {
            Write-Warning "A timer job with the name $($name) was not found"
        }
    }
    return $obj
}
